// ============================================================================
// FileName: StatelessProxyCore.cs
//
// Description:
// A stateless SIP proxy core to demonstrate SIP Proxy functionality.
//
// Author(s):
// Aaron Clauson
//
// License: 
// This software is licensed under the BSD License http://www.opensource.org/licenses/bsd-license.php
//
// Copyright (c) 2006 Aaron Clauson (aaronc@blueface.ie), Blue Face Ltd, Dublin, Ireland (www.blueface.ie)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that 
// the following conditions are met:
//
// Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 
// Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following 
// disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of Blue Face Ltd. 
// nor the names of its contributors may be used to endorse or promote products derived from this software without specific 
// prior written permission. 
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
// IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 
// OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
// OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
// POSSIBILITY OF SUCH DAMAGE.
// ============================================================================

using System;
using System.Collections.Generic;
using System.Data;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using CN.Sys.Net;
using BlueFace.VoIP.Authentication;
using CN.VoIP.Net;
using CN.VoIP.Net.SIP;
using CN.VoIP.SIPServer;
using IronPython.Compiler;
using IronPython.Hosting;
using log4net;

#if UNITTEST
using NUnit.Framework;
#endif

namespace CN.VoIP.Net.SIP
{	  
    public class StatelessProxyCore
	{       
        private static ILog logger = log4net.LogManager.GetLogger("sipproxy");

		private static string m_sipProxyUserAgent = SIPConstants.SIP_PROXYUSERAGENT_STRING;
		private static ProxyMonitorChannel m_monitorChannel = null;
        
        private PythonEngine m_pythonEngine = null;
        private CompiledCode m_compCode = null;

        private string m_proxyRuntimeCode;
        public string ProxyRuntimeCode
        {
            get { return m_proxyRuntimeCode; }
            set 
            {
                m_proxyRuntimeCode = value;
                m_compCode = m_pythonEngine.Compile(m_proxyRuntimeCode);
            }
        }

		public StatelessProxyCore(
            string proxyRuntimeCode,
            ProxyMonitorChannel monitorChannel)
		{
			try
			{
                m_monitorChannel = monitorChannel;
                m_proxyRuntimeCode = proxyRuntimeCode;

                // Configure Python engine.
                m_pythonEngine = new PythonEngine();

                m_pythonEngine.Execute("import clr");
                m_pythonEngine.Execute("clr.AddReference('System')");
                m_pythonEngine.Execute("clr.AddReference('CN.Sys')");
                m_pythonEngine.Execute("clr.AddReference('CN.VoIP.Net')");
                m_pythonEngine.Execute("clr.AddReference('CN.VoIP.SIPServerCores')");
                m_pythonEngine.Execute("from CN.Sys.Net import *");
                m_pythonEngine.Execute("from CN.VoIP.Net import *");
                m_pythonEngine.Execute("from CN.VoIP.Net.SIP import *");
                m_pythonEngine.Execute("from CN.VoIP.SIPServer import *");
                m_pythonEngine.Execute("from System.Text.RegularExpressions import *");

                try
                {
                    m_compCode = m_pythonEngine.Compile(m_proxyRuntimeCode);
                }
                catch (Exception configExcp)
                {
                    logger.Error("The proxy configuration is invalid please fix. " + configExcp.Message);
                }
			}
			catch(Exception excp)
			{
				logger.Error("Exception StatelessProxyCore (ctor). " + excp.Message);
				throw excp;
			}
		}
		
		/// <summary>
		///  From RFC3261: Stateless Proxy Request Processing:
		///  
		///  For each target, the proxy forwards the request following these
		///   1.  Make a copy of the received request
		///   2.  Update the Request-URI
		///   3.  Update the Max-Forwards header field
		///   4.  Optionally add a Record-route header field value
		///   5.  Optionally add additional header fields
		///   6.  Postprocess routing information
		///   7.  Determine the next-hop address, port, and transport
		///   8.  Add a Via header field value
		///   9.  Add a Content-Length header field if necessary
		///  10.  Forward the new request
		///  
		///  See sections 12.2.1.1 and 16.12.1.2 in the SIP RFC for the best explanation on the way the Route header works.
		/// </summary>
		/// <param name="sipRequest"></param>
		/// <param name="inEndPoint">End point the request was received on.</param>
		/// <param name="sipChannel"></param>
		public void GotRequest(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SIPRequest sipRequest)
		{
			try
			{
                DateTime startTime = DateTime.Now;
                
                // Used in the proxy monitor messages only, plays no part in request routing.
				string fromUser = (sipRequest.Header.From != null) ? sipRequest.Header.From.FromURI.User : null;
				string toUser = (sipRequest.Header.To != null) ? sipRequest.Header.To.ToURI.User : null;
				string summaryStr = "req " + sipRequest.Method + " from=" + fromUser + ", to=" + toUser + ", " + IPSocket.GetSocketString(remoteEndPoint);

                lock (this)
                {
                    m_pythonEngine.Globals["localEndPoint"] = localEndPoint;
                    m_pythonEngine.Globals["sipRequest"] = sipRequest;
                    m_pythonEngine.Globals["sipResponse"] = null;
                    m_pythonEngine.Globals["remoteEndPoint"] = remoteEndPoint;
                    m_pythonEngine.Globals["summary"] = summaryStr;

                    m_compCode.Execute();
                }

                double processingTime = DateTime.Now.Subtract(startTime).TotalMilliseconds;
                if (processingTime > 20)
                {
                    logger.Debug("GotRequest processing time=" + processingTime.ToString("0.##") + "ms.");
                }
			}
			catch(Exception excp)
			{
				string reqExcpError = "Exception StatelessProxyCore GotRequest. " + excp.Message;
				logger.Error(reqExcpError);
				ProxyMonitorEvent reqExcpEvent = new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, ProxyEventTypesEnum.Error, reqExcpError, localEndPoint, remoteEndPoint, null);
				SendMonitorEvent(reqExcpEvent);

				throw excp;
			}
		}	
		
		/// <summary>
		///  From RFC3261: Stateless Proxy Response Processing:
		///  
		/// When a response arrives at a stateless proxy, the proxy MUST inspect the sent-by value in the first
		/// (topmost) Via header field value.  If that address matches the proxy, (it equals a value this proxy has 
		/// inserted into previous requests) the proxy MUST remove that header field value from the response and  
		/// forward the result to the location indicated in the next Via header field value.  The proxy MUST NOT add 
		/// to, modify, or remove the message body.  Unless specified otherwise, the proxy MUST NOT remove
		/// any other header field values.  If the address does not match the  proxy, the message MUST be silently discarded.
		/// </summary>
		public void GotResponse(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SIPResponse sipResponse)
		{
			try
			{
                DateTime startTime = DateTime.Now;
                
                // Used in the proxy monitor messages only, plays no part in response processing.
				string fromUser = (sipResponse.Header.From != null) ? sipResponse.Header.From.FromURI.User : null;
				string toUser = (sipResponse.Header.To != null) ? sipResponse.Header.To.ToURI.User : null;
				string summaryStr = "resp " + sipResponse.Header.CSeqMethod + " from=" + fromUser + ", to=" + toUser + ", " + IPSocket.GetSocketString(remoteEndPoint);

                lock (this)
                {
                    m_pythonEngine.Globals["localEndPoint"] = localEndPoint;
                    m_pythonEngine.Globals["sipRequest"] = null;
                    m_pythonEngine.Globals["sipResponse"] = sipResponse;
                    m_pythonEngine.Globals["remoteEndPoint"] = remoteEndPoint;
                    m_pythonEngine.Globals["summary"] = summaryStr;

                    m_compCode.Execute();
                }

                double processingTime = DateTime.Now.Subtract(startTime).TotalMilliseconds;
                if (processingTime > 20)
                {
                    logger.Debug("GotResponse processing time=" + processingTime.ToString("0.##") + "ms.");
                }
			}
			catch(Exception excp)
			{
				string respExcpError = "Exception StatelessProxyCore GotResponse. " + excp.Message;
				logger.Error(respExcpError);
				ProxyMonitorEvent respExcpEvent = new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, ProxyEventTypesEnum.Error, respExcpError, localEndPoint, remoteEndPoint, null);
				SendMonitorEvent(respExcpEvent);

				throw excp;
			}
		}

        /// <summary>
        /// Helper method for dynamic proxy runtime script.
        /// </summary>
        /// <param name="responseCode"></param>
        /// <param name="localEndPoint"></param>
        /// <param name="remoteEndPoint"></param>
        /// <param name="sipRequest"></param>
        public static void Respond(SIPResponseStatusCodesEnum responseCode, IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SIPRequest sipRequest)
        {
            string fromUser = (sipRequest.Header.From != null) ? sipRequest.Header.From.FromURI.User : null;
            string toUser = (sipRequest.Header.To != null) ? sipRequest.Header.To.ToURI.User : null;
            string summaryStr = "req " + sipRequest.Method + " from=" + fromUser + ", to=" + toUser + ", " + IPSocket.GetSocketString(remoteEndPoint);

            string respondMessage = "Proxy responding with method " + responseCode + " for " + summaryStr + ".";
            ProxyMonitorEvent respondEvent = new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, ProxyEventTypesEnum.ProxyForward, respondMessage, localEndPoint, remoteEndPoint, remoteEndPoint);
            SendMonitorEvent(respondEvent);

            SIPResponse response = GetProxyResponse(sipRequest.Header, responseCode);
            SIPTransport.SendResponseFrom(localEndPoint, remoteEndPoint, response);
        }

        private SIPResponse GetOkResponse(SIPHeader requestHeader)
        {
            try
            {
                SIPResponse okResponse = new SIPResponse(SIPResponseStatusCodesEnum.Ok);

                okResponse.Header = new SIPHeader(requestHeader.Contact, requestHeader.From, requestHeader.To, requestHeader.CSeq, requestHeader.CallId);
                okResponse.Header.CSeqMethod = requestHeader.CSeqMethod;
                okResponse.Header.Via = requestHeader.Via;
                okResponse.Header.Server = m_sipProxyUserAgent;
                okResponse.Header.MaxForwards = Int32.MinValue;

                return okResponse;
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetProxyResponse. " + excp.Message);
                throw excp;
            }
        }

        private SIPResponse GetRedirectResponse(SIPHeader requestHeader, SIPContactHeader redirectContact)
        {
            try
            {
                SIPResponse redirectResponse = new SIPResponse(SIPResponseStatusCodesEnum.MovedTemporarily);
                redirectResponse.Header = new SIPHeader(redirectContact, requestHeader.From, requestHeader.To, requestHeader.CSeq, requestHeader.CallId);
                redirectResponse.Header.CSeqMethod = requestHeader.CSeqMethod;
                redirectResponse.Header.Via = requestHeader.Via;
                //redirectResponse.Header.Server = m_sipProxyUserAgent;
                redirectResponse.Header.MaxForwards = Int32.MinValue;

                return redirectResponse;
            }
            catch (Exception excp)
            {
                logger.Error("Exception GetRedirectResponse. " + excp.Message);
                throw excp;
            }
        }

		private static SIPResponse GetProxyResponse(SIPHeader requestHeader, SIPResponseStatusCodesEnum responseCode)
		{	
			try
			{
				SIPResponse proxyResponse = new SIPResponse(responseCode);
			
				proxyResponse.Header = new SIPHeader(requestHeader.From, requestHeader.To, requestHeader.CSeq, requestHeader.CallId);
				proxyResponse.Header.CSeqMethod = requestHeader.CSeqMethod;
				proxyResponse.Header.Via = requestHeader.Via;
				proxyResponse.Header.Server = m_sipProxyUserAgent;
				proxyResponse.Header.MaxForwards = Int32.MinValue;

				return proxyResponse;
			}
			catch(Exception excp)
			{
				logger.Error("Exception GetProxyResponse. " + excp.Message);
				throw excp;
			}
		}

        /// <summary>
        /// Helper method for dynamic proxy runtime script.
        /// </summary>
        /// <param name="eventType"></param>
        /// <param name="message"></param>
        /// <param name="localEndPoint"></param>
        /// <param name="remoteEndPoint"></param>
        /// <param name="dstEndPoint"></param>
        public static void SendMonitorEvent(ProxyEventTypesEnum eventType, string message, IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, IPEndPoint dstEndPoint)
        {
            ProxyMonitorEvent proxyEvent = new ProxyMonitorEvent(ProxyServerTypesEnum.StatelessProxy, eventType, message, localEndPoint, remoteEndPoint, dstEndPoint);
            SendMonitorEvent(proxyEvent);
        }
	
		private static void SendMonitorEvent(ProxyMonitorEvent monitorEvent)
		{
			if(m_monitorChannel != null)
			{
				m_monitorChannel.Send(monitorEvent);
			}
		}
	
		#region Unit testing.

		#if UNITTEST

		#region Mock Objects

		/*public class ProxySIPChannel
		{		
			public IPEndPoint m_unitTestEndPoint;	// Used so that tests using this mock object can see the end point a message was sent to.

			public ProxySIPChannel()
			{

			}
		
			public ProxySIPChannel(IPEndPoint endPoint)
			{

			}
		
			public void Send(IPEndPoint endPoint, SIPResponse sipResponse)
			{
				m_unitTestEndPoint = endPoint;
				
				Console.WriteLine("Dummy Channel: Sending to: " + endPoint.Address.ToString() + ":" + endPoint.Port + "\r\n" + sipResponse.ToString());
			}

			public void Send(IPEndPoint endPoint, SIPRequest sipRequest)
			{
				m_unitTestEndPoint = endPoint;
				
				Console.WriteLine("Dummy Channel: Sending to: " + endPoint.Address.ToString() + ":" + endPoint.Port + "\r\n" + sipRequest.ToString());
			}
		}*/
			

		#endregion

		/*
		[TestFixture]
		public class StatelessProxyCoreUnitTest
		{
			private static string m_CRLF = SIPConstants.CRLF;
			
			private static XmlDocument m_sipRoutes = new XmlDocument();
			
			[TestFixtureSetUp]
			public void Init()
			{
				string sipRoutesXML = 
					"<siproutes>" +
					" <inexsipserversocket>192.168.1.28:5061</inexsipserversocket>" +
					" <sipserversocket>192.168.1.28:5061</sipserversocket>" +
					"</siproutes>";
				m_sipRoutes.LoadXml(sipRoutesXML);
			}

			[TestFixtureTearDown]
			public void Dispose()
			{			
				
			}

			[Test]
			public void SampleTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				Assert.IsTrue(true, "True was false.");

				Console.WriteLine("---------------------------------"); 
			}

			
			[Test]
				//[Ignore("Next hop tests only.")]
			public void ResponseAddViaHeaderTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				
				Assert.IsTrue(true, "True was false.");

				Console.WriteLine("---------------------------------"); 
			}

			
		
			[Test]
			//[Ignore("Next hop tests only.")]
			public void ParseRouteNotForThisProxyBYEUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);

				string sipRequest = 
					"BYE sip:bluesipd@192.168.1.2:5065 SIP/2.0" + m_CRLF +
					"Via: SIP/2.0/UDP 213.168.225.133:5060;branch=z9hG4bK74ab714b;rport" + m_CRLF +	
					"From: <sip:303@bluesipd>;tag=as6a65fae3" + m_CRLF +
					"To: bluesipd <sip:bluesipd@bluesipd:5065>;tag=1898247079" + m_CRLF +
					"Contact: <sip:303@213.168.225.133>" + m_CRLF +
					"Call-ID: 80B34165-8C89-4623-B862-40AFB1884071@192.168.1.2" + m_CRLF +
					"CSeq: 102 BYE" + m_CRLF +
					"Route: <sip:bluesipd@12.12.12.12:5065;lr>" + m_CRLF +
					"Content-Length: 0" + m_CRLF+ m_CRLF;

				SIPMessage sipMsg = SIPMessage.ParseSIPMessage(Encoding.ASCII.GetBytes(sipRequest), new IPEndPoint(IPAddress.Loopback, 9998));
				SIPRequest byeReq = SIPRequest.ParseSIPRequest(sipMsg);
				
				IPEndPoint proxyEndPoint = new IPEndPoint(IPAddress.Loopback, 19998);
				ProxySIPChannel dummySIPChannel = new ProxySIPChannel(proxyEndPoint, null);
				StatelessProxyCore proxyCore = new StatelessProxyCore(dummySIPChannel, proxyEndPoint, null, null, true, null, null);

				IPEndPoint dummyRcvdEndPoint = new IPEndPoint(IPAddress.Loopback, 19999);
				proxyCore.GotRequest(proxyEndPoint, dummyRcvdEndPoint, byeReq);

				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Address.ToString() == "12.12.12.12", "The IP address for the UA end point was not correctly extracted, extracted address " + IPSocketAddress.GetSocketString(dummySIPChannel.m_unitTestEndPoint) + ".");
				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Port == 5065, "The IP port for the UA end point was not correctly extracted.");
				Assert.IsTrue(byeReq.URI.ToString() == "sip:bluesipd@192.168.1.2:5065", "The SIP URI was incorrect.");
				Assert.IsTrue(byeReq.Header.Routes.TopRoute.ToString() == "<sip:bluesipd@12.12.12.12:5065;lr>", "The top route was incorrect.");

				Console.WriteLine("-----------------------------------------");
			}

			[Test]
			//[Ignore("Next hop tests only.")]
			public void ParseStrictRouteNotForThisProxyBYEUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);

				string sipRequest = 
					"BYE sip:bluesipd@192.168.1.2:5065 SIP/2.0" + m_CRLF +
					"Via: SIP/2.0/UDP 213.168.225.133:5060;branch=z9hG4bK74ab714b;rport" + m_CRLF +	
					"From: <sip:303@bluesipd>;tag=as6a65fae3" + m_CRLF +
					"To: bluesipd <sip:bluesipd@bluesipd:5065>;tag=1898247079" + m_CRLF +
					"Contact: <sip:303@213.168.225.133>" + m_CRLF +
					"Call-ID: 80B34165-8C89-4623-B862-40AFB1884071@192.168.1.2" + m_CRLF +
					"CSeq: 102 BYE" + m_CRLF +
					"Route: <sip:bluesipd@12.12.12.12:5065>" + m_CRLF +
					"Content-Length: 0" + m_CRLF+ m_CRLF;

				SIPMessage sipMsg = SIPMessage.ParseSIPMessage(Encoding.ASCII.GetBytes(sipRequest), new IPEndPoint(IPAddress.Loopback, 9998));
				SIPRequest byeReq = SIPRequest.ParseSIPRequest(sipMsg);
				
				IPEndPoint proxyEndPoint = new IPEndPoint(IPAddress.Loopback, 19998);
				ProxySIPChannel dummySIPChannel = new ProxySIPChannel(proxyEndPoint);
				StatelessProxyCore proxyCore = new StatelessProxyCore(dummySIPChannel, proxyEndPoint, null, null, true, null, null);

				IPEndPoint dummyRcvdEndPoint = new IPEndPoint(IPAddress.Loopback, 19999);
				proxyCore.GotRequest(proxyEndPoint, dummyRcvdEndPoint, byeReq);

				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Address.ToString() == "12.12.12.12", "The IP address for the UA end point was not correctly extracted, extracted address " + IPSocketAddress.GetSocketString(dummySIPChannel.m_unitTestEndPoint) + ".");
				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Port == 5065, "The IP port for the UA end point was not correctly extracted.");
				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Address.ToString() == "12.12.12.12", "The IP address for the UA end point was not correctly extracted, extracted address " + IPSocketAddress.GetSocketString(dummySIPChannel.m_unitTestEndPoint) + ".");
				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Port == 5065, "The IP port for the UA end point was not correctly extracted.");
				Assert.IsTrue(byeReq.Header.Routes.TopRoute.ToString() == "sip:bluesipd@192.168.1.2:5065", "The SIP URI was incorrect.");
				Assert.IsTrue(byeReq.URI.ToString() == "<sip:bluesipd@12.12.12.12:5065;lr>", "The top route was incorrect.");

				Console.WriteLine("-----------------------------------------");
			}

			[Test]
				//[Ignore("Next hop tests only.")]
			public void ParseRouteForThisProxyBYEUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);

				IPEndPoint proxyEndPoint = new IPEndPoint(IPAddress.Loopback, 19998);

				string sipRequest = 
					"BYE sip:bluesipd@192.168.1.2:5065 SIP/2.0" + m_CRLF +
					"Via: SIP/2.0/UDP 213.168.225.133:5060;branch=z9hG4bK74ab714b;rport" + m_CRLF +	
					"From: <sip:303@bluesipd>;tag=as6a65fae3" + m_CRLF +
					"To: bluesipd <sip:bluesipd@bluesipd:5065>;tag=1898247079" + m_CRLF +
					"Contact: <sip:303@213.168.225.133>" + m_CRLF +
					"Call-ID: 80B34165-8C89-4623-B862-40AFB1884071@192.168.1.2" + m_CRLF +
					"CSeq: 102 BYE" + m_CRLF +
					"Route: <sip:" + IPSocketAddress.GetSocketString(proxyEndPoint) + ";lr>" + m_CRLF +
					"Content-Length: 0" + m_CRLF+ m_CRLF;

				SIPMessage sipMsg = SIPMessage.ParseSIPMessage(Encoding.ASCII.GetBytes(sipRequest), new IPEndPoint(IPAddress.Loopback, 9998));
				SIPRequest byeReq = SIPRequest.ParseSIPRequest(sipMsg);
				
				ProxySIPChannel dummySIPChannel = new ProxySIPChannel(proxyEndPoint);
				StatelessProxyCore proxyCore = new StatelessProxyCore(dummySIPChannel, proxyEndPoint, null, null, true, null, null);

				IPEndPoint dummyRcvdEndPoint = new IPEndPoint(IPAddress.Loopback, 19999);
				proxyCore.GotRequest(proxyEndPoint, dummyRcvdEndPoint, byeReq);

				Assert.IsTrue(byeReq.URI.ToString() == "sip:bluesipd@192.168.1.2:5065", "The SIP URI was incorrect.");
				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Address.ToString() == "12.12.12.12", "The IP address for the UA end point was not correctly extracted, extracted address " + IPSocketAddress.GetSocketString(dummySIPChannel.m_unitTestEndPoint) + ".");
				//Assert.IsTrue(dummySIPChannel.m_unitTestEndPoint.Port == 5065, "The IP port for the UA end point was not correctly extracted.");

				Console.WriteLine("-----------------------------------------");
			}

			[Test]
			//[Ignore("Next hop tests only.")]
			public void TooManyHopsUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);

				string sipRequest = 
					"BYE sip:bluesipd@192.168.1.2:5065 SIP/2.0" + m_CRLF +
					"Via: SIP/2.0/UDP 213.168.225.133:5060;branch=z9hG4bK74ab714b;rport" + m_CRLF +
					"Route: <sip:bluesipd@12.12.12.12:5065>" + m_CRLF +
					"From: <sip:303@bluesipd>;tag=as6a65fae3" + m_CRLF +
					"To: bluesipd <sip:bluesipd@bluesipd:5065>;tag=1898247079" + m_CRLF +
					"Contact: <sip:303@213.168.225.133>" + m_CRLF +
					"Call-ID: 80B34165-8C89-4623-B862-40AFB1884071@192.168.1.2" + m_CRLF +
					"CSeq: 102 BYE" + m_CRLF +
					"Max-Forwards: 0" + m_CRLF +
					"User-Agent: asterisk" + m_CRLF +
					"Content-Length: 0" + m_CRLF+ m_CRLF;

				SIPMessage sipMsg = SIPMessage.ParseSIPMessage(Encoding.ASCII.GetBytes(sipRequest), new IPEndPoint(IPAddress.Loopback, 9998));
				SIPRequest byeReq = SIPRequest.ParseSIPRequest(sipMsg);
				
				IPEndPoint proxyEndPoint = new IPEndPoint(IPAddress.Loopback, 19998);
				ProxySIPChannel dummySIPChannel = new ProxySIPChannel(proxyEndPoint);
				StatelessProxyCore proxyCore = new StatelessProxyCore(dummySIPChannel, proxyEndPoint, null, null, true, null, null);

				IPEndPoint dummyRcvdEndPoint = new IPEndPoint(IPAddress.Loopback, 19999);
				proxyCore.GotRequest(proxyEndPoint, dummyRcvdEndPoint, byeReq);

				//Assert.IsTrue(dummyChannel.m_unitTestEndPoint.Address.ToString() == "12.12.12.12", "The IP address for the UA end point was not correctly extracted, extracted address " + dummyChannel.m_unitTestEndPoint.Address.ToString() + ".");
				//ssert.IsTrue(dummyChannel.m_unitTestEndPoint.Port == 5065, "The IP port for the UA end point was not correctly extracted.");

				Console.WriteLine("-----------------------------------------");
			}
	
			[Test]
			public void DetermineHextHopUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				StatelessProxyCore proxy = new StatelessProxyCore(null, 0);
				string uri = "sip:303@213.168.225.133";
				IPEndPoint nextHop = proxy.DetermineNextHop(uri);
				Console.WriteLine("The next hop for uri = " + uri + " is " + nextHop.Address.ToString() + ":" + nextHop.Port + ".");
				Assert.IsTrue(nextHop.Address.ToString() == "213.168.225.133", "The next hop IP address was not correctly determined from the request URI.");
				Assert.IsTrue(nextHop.Port == 5060, "The next hop port was not correctly determined from the request URI.");
			}


			[Test]
			public void DetermineHextHopDifferentServerUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				StatelessProxyCore proxy = new StatelessProxyCore(null, 0);
				string uri = "sip:303@213.168.225.135";
				IPEndPoint nextHop = proxy.DetermineNextHop(uri);
				Console.WriteLine("The next hop for uri = " + uri + " is " + nextHop.Address.ToString() + ":" + nextHop.Port + ".");
				Assert.IsTrue(nextHop.Address.ToString() == "213.168.225.135", "The next hop IP address was not correctly determined from the request URI.");
				Assert.IsTrue(nextHop.Port == 5060, "The next hop port was not correctly determined from the request URI.");
			}

		
			[Test]
			public void DetermineHextHopWithPortUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				StatelessProxyCore proxy = new StatelessProxyCore(null, 0);
				string uri = "sip:303@213.168.225.133:5066";
				IPEndPoint nextHop = proxy.DetermineNextHop(uri);
				Console.WriteLine("The next hop for uri = " + uri + " is " + nextHop.Address.ToString() + ":" + nextHop.Port + ".");
				Assert.IsTrue(nextHop.Address.ToString() == "213.168.225.133", "The next hop IP address was not correctly determined from the request URI.");
				Assert.IsTrue(nextHop.Port== 5066, "The next hop port was not correctly determined from the request URI.");
			}

		
			[Test]
			public void DetermineHextHopWithInvalidURIUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				StatelessProxyCore proxy = new StatelessProxyCore(null, 0);
				string uri = "sip:303@myserver";
				IPEndPoint nextHop = proxy.DetermineNextHop(uri);
				Console.WriteLine("The next hop for uri = " + uri + " is " + nextHop.Address.ToString() + ":" + nextHop.Port + ".");
				Assert.IsTrue(nextHop.Address.ToString() == "213.168.225.133", "The next hop IP address was not correctly determined from the request URI.");
				Assert.IsTrue(nextHop.Port== 5060, "The next hop port was not correctly determined from the request URI.");
			}

			[Test]
			public void CredentialsPassThruUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				string registerMsg = 
					"REGISTER sip:blueface SIP/2.0" + m_CRLF +
					"Via: SIP/2.0/UDP 192.168.1.2:5066;branch=z9hG4bK60341d2470" + m_CRLF +
					"From: \"aaron\" <sip:aaron@blueface>;tag=632743740325493750" + m_CRLF +
					"To: \"aaron\" <sip:aaron@blueface>" + m_CRLF +
					"Contact: <sip:aaron@220.240.255.198:50652>" + m_CRLF +
					"Call-ID: 1540e4c48d3b447@192.168.1.2" + m_CRLF +
					"CSeq: 550 REGISTER" + m_CRLF +
					"Max-Forwards: 70" + m_CRLF +
					"Expires: 600" + m_CRLF +
					"Authorization: Digest username=\"aaron\",realm=\"asterisk\",nonce=\"422e215b\",response=\"af05b4f63e3593c449ad9eac9f0443e4\",uri=\"sip:blueface\"" + m_CRLF + m_CRLF;
				StatelessProxyCore proxy = new StatelessProxyCore(null, 0);
				SIPRequest registerReq = SIPRequest.ParseSIPRequest(registerMsg);
				
				StatelessProxyCore proxyCore = new StatelessProxyCore("213.168.225.135", 5060);

				SIPChannel dummyChannel = new SIPChannel();
				proxyCore.GotRequest(registerReq, new IPEndPoint(IPAddress.Parse("12.12.12.12"), 5060), dummyChannel);

				Console.WriteLine("-----------------------------------------");	
			}

			[Test]
			public void CredentialsPassThruNonceAndRealmResposeUnitTest()
			{
				Console.WriteLine("--> " + System.Reflection.MethodBase.GetCurrentMethod().Name);
				string unauthRespStr = 
					"SIP/2.0 401 Unauthorized" + m_CRLF +
					"Via: SIP/2.0/UDP 213.168.225.135:5060;branch=z9hG4bKbaec65a4f8" + m_CRLF +
					"Via: SIP/2.0/UDP 192.168.1.2:5066;received=220.240.255.198:63994;branch=z9hG4bKbaec65a4f9" + m_CRLF +
					"From: \"aaron\" <sip:aaron@blueface>;tag=632743740325493750" + m_CRLF +
					"To: \"aaron\" <sip:aaron@blueface>;tag=as7b152585" + m_CRLF +
					"Contact: <sip:aaron@213.168.225.133>" + m_CRLF +
					"Call-ID: 1540e4c48d3b447@192.168.1.2" + m_CRLF +
					"CSeq: 549 REGISTER" + m_CRLF +
					"Max-Forwards: 70" + m_CRLF +
					"User-Agent: asterisk" + m_CRLF +
					"WWW-Authenticate: Digest realm=\"asterisk\",nonce=\"422e215b\"" + m_CRLF +
					"Record-Route: <sip:213.168.225.136:6060;lr>" + m_CRLF +
					"Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, NOTIFY" + m_CRLF + m_CRLF;
				StatelessProxyCore proxy = new StatelessProxyCore(null, 0);
				SIPResponse unauthResp = SIPResponse.ParseSIPResponse(unauthRespStr);
				
				StatelessProxyCore proxyCore = new StatelessProxyCore("213.168.225.135", 5060);

				SIPChannel dummyChannel = new SIPChannel();
				proxyCore.GotResponse(unauthResp, new IPEndPoint(IPAddress.Parse("12.12.12.12"), 5060), dummyChannel);

				Console.WriteLine("-----------------------------------------");	
			}						
		}
		*/
		#endif

		#endregion
	}
}
